//!
/**
 ***********************************************************************************************************
 * <RBVisibilityManager implementation>
 ***********************************************************************************************************
 * @file RBVisibilityManager.cpp
 ***********************************************************************************************************/

/************************************************************************************************************
 * Includes
 ************************************************************************************************************/
#include "RBMainPCH.h"

#include "RBVisibilityManager.h"

#include "RBBVolume.h"
#include "RBCamera.h"
#include "RBEntity.h"
#include "RBMathVector.h"
#include "RBPlane.h"
#include "RBVisibilityComponent.h"
#include "RBVector.h"

#include "RBRenderPassDebug.h"

/************************************************************************************************************
 * Macros
 ************************************************************************************************************/

/************************************************************************************************************
 * Classes implementation
 ************************************************************************************************************/

/************************************************************************************************************/

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBVisibilityManager::RBVisibilityManager()
{
	for (uint32 ui=0; ui < max_sync_points; ++ui)
	{
		m_sync_points[ui] = stb_sync_new();
	}
	
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBVisibilityManager::~RBVisibilityManager()
{
	for (uint32 ui=0; ui < max_sync_points; ++ui)
	{
		stb_sync_delete(m_sync_points[ui]);
	}
}

void* intersect_spheres_frustum(void* data)
{
	RBVisibilityManager::rb_intersect_params* params = (RBVisibilityManager::rb_intersect_params*)data;
	RBMath::Intersect::BoxesVsFrustum(params->results, params->frustum_planes, params->boxes + params->start, params->count);
	return NULL;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBVisibilityManager::start_visibility( const RBCamera* camera, uint32 sync_point )
{

    uint32 uiCurNodeIdx=0;

	uint32 job_count = m_aComponents.empty() ? 0 : m_aComponents.size()/max_objects_per_batch+1;

	m_params[sync_point].reserve(job_count);
	stb_sync_set_target(m_sync_points[sync_point], job_count+1);

	RBPlane planes[6];
	float4x4 viewProjection = camera->GetProjMatrix() * camera->GetViewMatrix();
	RBMath::ComputeFrustumPlanes(planes, viewProjection);


    while (uiCurNodeIdx < m_aComponents.size())
    {
        uint32 count = RBMath::Min<uint32>(m_aComponents.size()-uiCurNodeIdx, max_objects_per_batch);
		rb_intersect_params params;
		memcpy(params.frustum_planes, planes, sizeof(params.frustum_planes));
		params.boxes = &m_aComponents[0];
		params.start = uiCurNodeIdx;
		params.count = count;
		m_params[sync_point].push_back(params);
		stb_work_reach(intersect_spheres_frustum, &m_params[sync_point].back(),NULL,m_sync_points[sync_point]);
        uiCurNodeIdx += count;
    }
}


void RBVisibilityManager::sync_visibility_results( std::vector<RBBVolumeHandle>* nodes, uint32 sync_mask )
{
	for (uint32 ui=0; ui<max_sync_points; ++ui)
	{
		if (sync_mask & (1<<ui))
		{
			stb_sync_reach_and_wait(m_sync_points[ui]);
			std::vector<rb_intersect_params>& cur_results = m_params[ui];
			for (uint32 result_idx=0; result_idx < cur_results.size(); ++result_idx)
			{
				rb_intersect_params& inputs = cur_results[result_idx];
				uint32 start_idx = inputs.start;
				for (uint32 geom_idx=0; geom_idx < inputs.count; ++geom_idx)
				{
					if (inputs.results[geom_idx] != -1 )
					{
						nodes->push_back(m_aComponents[start_idx + geom_idx]);
					}
				}
			}
			m_params[ui].clear();
		}
	}

}


/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBVisibilityManager::AddEntity( RBEntityHandle pEntity )
{
    RBEntity& entity = RBEntity::Get(pEntity);

    uint32 cmpCount =  entity.GetComponentCount<RBBVolume>();

    for (uint16 idx=0; idx<cmpCount; ++idx)
    {
        RBBVolumeHandle visibleNode = entity.GetComponent<RBBVolume>(idx);
        m_aComponents.push_back(visibleNode);
    }

}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBVisibilityManager::RemoveEntity( RBEntityHandle pEntity )
{
    RBEntity& entity = RBEntity::Get(pEntity);
    uint32 cmpCount =  entity.GetComponentCount<RBBVolume>();

    for (uint16 idx=0; idx<cmpCount; ++idx)
    {
        RBBVolumeHandle visibleNode = entity.GetComponent<RBBVolume>(idx);
        std::remove(m_aComponents.begin(), m_aComponents.end(), visibleNode);
    }
}
